package main

import (
	"github.com/unknwon/goconfig"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

var dir = "docs"

func main() {
	initConfig()
	http.Handle("/css/", http.FileServer(http.Dir(dir)))
	http.Handle("/font-awesome/", http.FileServer(http.Dir(dir)))
	http.Handle("/js/", http.FileServer(http.Dir(dir)))
	http.Handle("/layui/", http.FileServer(http.Dir(dir)))
	http.Handle("/image/", http.FileServer(http.Dir(dir)))
	http.Handle("/font/", http.FileServer(http.Dir(dir)))
	http.Handle("/doc/", http.FileServer(http.Dir("blog")))
	http.Handle("/pic/", http.FileServer(http.Dir("pic")))
	http.HandleFunc("/index.html", index)
	http.HandleFunc("/", index)
	http.HandleFunc("/article.html", article)
	http.HandleFunc("/diary.html", diary)
	http.HandleFunc("/picIndex.html", picIndex)
	http.HandleFunc("/link.html", link)
	http.HandleFunc("/message.html", message)
	http.HandleFunc("/read.html", read)
	http.HandleFunc("/remark", remark)
	http.HandleFunc("/message2", message2)
	http.HandleFunc("/editDiary", editDiaryIndex)
	http.HandleFunc("/edit_diary", editDiary)

	go deleteAccess()
	time.Sleep(2 * time.Second)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

var diaryLock sync.Mutex

func editDiary(w http.ResponseWriter, r *http.Request) {
	_ = r.MultipartForm
	//日记
	tokenStr := r.FormValue("token")
	remarkContent := r.FormValue("editorContent")
	if token == "" || remarkContent == "" {
		handle(w, r, "edit_diary", nil)
		return
	}
	md5str := md5V2(tokenStr)
	if md5str != token {
		handle(w, r, "edit_diary", nil)
		return
	}
	diaryLock.Lock()
	defer diaryLock.Unlock()
	t := time.Now()
	date := t.Format("20060102")
	year := date[0:4]
	mothDay := date[4:8]
	month := mothDay[0:2]
	day := mothDay[2:4]
	dateTitle := month + "月 " + day + "日"
	exists, err := PathExists(diaryDir + "/" + year)
	if err != nil {
		http.Redirect(w, r, "diary.html", http.StatusFound)
		return
	}
	if !exists {
		_ = os.Mkdir(diaryDir+"/"+year, os.ModePerm)
	}
	var diaryContent string
	//处理上传的图片
	picName := time.Now().Format("20060102150405") + ".png"
	file, _, err := r.FormFile("file")
	if err == nil && file != nil {
		defer file.Close()
		out, err := os.Create(picDir + "/" + picName)
		defer out.Close()
		if err == nil {
			_, err = io.Copy(out, file)
			imgSrc := "<img src=\"/pic/" + picName + "\">"
			diaryContent = "<date>" + dateTitle + "</date>" + "<dairy>" + remarkContent + imgSrc + "</dairy>"
		}
	} else {
		diaryContent = "<date>" + dateTitle + "</date>" + "<dairy>" + remarkContent + "</dairy>"
	}
	_ = ioutil.WriteFile(diaryDir+"/"+year+"/"+mothDay+".txt", []byte(diaryContent), 0777)
	http.Redirect(w, r, "diary.html", http.StatusFound)
}

var diaryDir = "diary"
var picDir = "pic/pic"

func editDiaryIndex(w http.ResponseWriter, r *http.Request) {
	handle(w, r, "edit_diary", nil)
}

/**
  编辑日记
*/
var token string
var port = "9090"

func initConfig() {
	go func() {
		for {
			cfg, err := goconfig.LoadConfigFile("server.ini")
			PanicIfErrMsg(err, "找不到配置文件 config.ini")
			token, _ = cfg.GetValue("server", "token")
			port, _ = cfg.GetValue("server", "port")
			blogDir, _ = cfg.GetValue("server", "blogPath")
			time.Sleep(2 * time.Minute)
		}
	}()
}

func index(w http.ResponseWriter, r *http.Request) {
	recommends, blogs := blogList()
	if len(blogs) > 3 {
		blogs = blogs[:3]
	}
	handle(w, r, "index", BlogV{
		BlogInfos:  blogs,
		Recommends: recommends,
	})
}

type BlogInfo struct {
	Name         string
	Title        string
	Year         string
	Moth         string
	Day          string
	TitleImg     string
	BlogId       string
	Content      template.HTML
	LastEditDate string
	Remarks      []Message
	RemarkCount  int
	No           int
}

type Recommend struct {
	BlogId   string
	BlogName string
}

type Message struct {
	Name string
	Msg  template.HTML
}

type BlogV struct {
	BlogInfos  []No
	Recommends []Recommend
}

type PicTable struct {
	PicImgSrc []string
}

type PicCell struct {
	ThImageSrc string
	No         int
}

func (t PicCell) GetNo() int {
	return t.No
}

func picIndex(w http.ResponseWriter, r *http.Request) {

	var cells []No
	fileInfos, err := ioutil.ReadDir(picDir)
	if err != nil {
		handle(w, r, "picIndex", nil)
		return
	}

	for i := range fileInfos {
		picFile := fileInfos[i]
		fileName := strings.Split(picFile.Name(), ".")[0]
		no, _ := strconv.Atoi(fileName)
		cells = append(cells, PicCell{
			ThImageSrc: "/pic/" + picFile.Name(),
			No:         no,
		})
	}
	Sort(cells)

	var data []PicTable
	var imgSrcs []string
	for i := range cells {
		cell := cells[i].(PicCell)
		imgSrcs = append(imgSrcs, cell.ThImageSrc)
		if i == len(cells)-1 || len(imgSrcs) == 3 {
			data = append(data, PicTable{PicImgSrc: imgSrcs})
			imgSrcs = append(imgSrcs[:0], imgSrcs[len(imgSrcs):]...)
		}
	}
	handle(w, r, "picIndex", data)
}

func blogList() ([]Recommend, []No) {
	var recommends []Recommend
	var blogs []No
	fileInfos, err := ioutil.ReadDir(blogDir)
	if err != nil {
		return recommends, blogs
	}
	//遍历每一个博客目录
	for i := range fileInfos {
		dir := fileInfos[i]
		//博客目录路径
		filePath := blogDir + "/" + dir.Name()
		blogFiles, err := ioutil.ReadDir(filePath)
		if err != nil {
			continue
		}
		//博客内容
		var blog string
		var blogName string
		var titleImages string
		var RemarkCount int
		for j := range blogFiles {
			//博客目录下子目录  包含博客txt文件  图片文件得目录
			blogFile := blogFiles[j]

			if blogFile.Name() == "remark" {
				remarkBytes, err := ioutil.ReadFile(filePath + "/remark/remark.txt")
				if err != nil {
					continue
				}
				remarkTxt := string(remarkBytes)
				doc := Doc{content: remarkTxt}
				remarks := doc.finds("remark")
				RemarkCount = len(remarks)
				continue
			}

			//如果是文件
			if !blogFile.IsDir() {
				blogBytes, err := ioutil.ReadFile(filePath + "/" + blogFile.Name())
				if err != nil {
					continue
				}
				blog = string(blogBytes)
				blogName = blogFile.Name()
			}
			if blogFile.IsDir() && blogFile.Name() == "imgs" {
				titleImages = "/doc/" + dir.Name() + "/imgs/title.png"
			}
		}
		if len(blog) <= 0 {
			continue
		}
		blogName = strings.ReplaceAll(blogName, ".txt", "")
		recommends = append(recommends, Recommend{
			BlogId:   dir.Name(),
			BlogName: blogName,
		})
		doc := Doc{content: blog}
		title := doc.find("t")
		date := doc.find("date")
		dates := strings.Split(date, ".")
		lastEditDate := doc.find("lastEditDate")
		no, _ := strconv.Atoi(dir.Name())
		blogs = append(blogs, BlogInfo{
			Name:         blogName,
			Title:        title,
			Year:         dates[0],
			Moth:         dates[1],
			Day:          dates[2],
			TitleImg:     titleImages,
			BlogId:       dir.Name(),
			RemarkCount:  RemarkCount,
			LastEditDate: lastEditDate,
			No:           no,
		})
	}
	Sort(blogs)
	return recommends, blogs
}

var blogDir = "blog/doc"

func article(w http.ResponseWriter, r *http.Request) {
	recommends, blogs := blogList()
	handle(w, r, "article", BlogV{BlogInfos: blogs, Recommends: recommends})
}

type DiaryYear struct {
	Year   string
	Diarys []No
	No     int
}
type DiaryDay struct {
	Date  string
	Diary template.HTML
	No    int
}

func (t DiaryYear) GetNo() int {
	return t.No
}

func (t DiaryDay) GetNo() int {
	return t.No
}

func (t BlogInfo) GetNo() int {
	return t.No
}

type No interface {
	GetNo() int
}

func diary(w http.ResponseWriter, r *http.Request) {
	var diarys []No
	fileInfos, err := ioutil.ReadDir(diaryDir)
	if err != nil {
		handle(w, r, "diary", nil)
		return
	}
	//遍历每一个博客目录
	for i := range fileInfos {
		dir := fileInfos[i]
		//博客目录路径
		filePath := diaryDir + "/" + dir.Name()
		diaryFiles, err := ioutil.ReadDir(filePath)
		if err != nil {
			continue
		}
		year := dir.Name() //年份
		var diaryDays []No
		for j := range diaryFiles {
			//日记内容
			var diary string
			var diaryFileName int
			//博客目录下子目录  包含博客txt文件  图片文件得目录
			diaryFile := diaryFiles[j]

			//如果是文件
			if !diaryFile.IsDir() {
				diaryBytes, err := ioutil.ReadFile(filePath + "/" + diaryFile.Name())
				if err != nil {
					continue
				}
				diary = string(diaryBytes)
				tempFileName := strings.ReplaceAll(diaryFile.Name(), ".txt", "")
				diaryFileName, err = strconv.Atoi(tempFileName)
				if err != nil {
					log.Printf("diay err = %s\n", err.Error())
					continue
				}
				if len(diary) <= 0 {
					continue
				}
				doc := Doc{content: diary}
				diaryContent := doc.find("dairy")
				date := doc.find("date")
				//日记对象
				diaryday := DiaryDay{
					Date:  date,
					Diary: template.HTML(diaryContent),
					No:    diaryFileName,
				}
				diaryDays = append(diaryDays, diaryday)
			}
		}
		Sort(diaryDays)
		no, err := strconv.Atoi(year)
		if err != nil {
			log.Printf("diay year err = %s\n", err.Error())
			continue
		}
		dayYear := DiaryYear{
			Year:   year,
			Diarys: diaryDays,
			No:     no,
		}
		diarys = append(diarys, dayYear)
	}
	Sort(diarys)
	handle(w, r, "diary", diarys)
}

func link(w http.ResponseWriter, r *http.Request) {
	handle(w, r, "link", nil)
}

func message(w http.ResponseWriter, r *http.Request) {
	var messages []Message
	messageBytes, err := ioutil.ReadFile("message/message.txt")
	if err != nil {
		handle(w, r, "message", nil)
		return
	}
	messageTxt := string(messageBytes)
	doc := Doc{content: messageTxt}
	remarks := doc.finds("remark")
	for _, remark := range remarks {
		tDoc := Doc{remark}
		messages = append(messages, Message{
			Name: tDoc.find("name"),
			Msg:  template.HTML(tDoc.find("msg")),
		})
	}
	handle(w, r, "message", messages)
}

func read(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	blogId := getFormValue("blogId", r)
	var blogV BlogInfo
	if blogId != "" {
		blogDoc := BlogDoc{}
		err := blogDoc.Init(blogId)
		if err != nil {
			handle(w, r, "read", nil)
		}
		blogV = blogDoc.BlogContent()
	}
	handle(w, r, "read", blogV)
}

var remarkSyn sync.Mutex

func remark(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm()
	blogId := getFormValue("blogId", r)
	if !checkAccess(r) {
		http.Redirect(w, r, "read.html?blogId="+blogId, http.StatusFound)
		return
	}
	//评论
	name := r.FormValue("name")
	remarkContent := r.FormValue("remarkContent")
	if len(remarkContent) > 300 {
		http.Redirect(w, r, "read.html?blogId="+blogId, http.StatusFound)
		return
	}
	t := time.Now().Format("2006-01-02 15:04:05")
	if name == "" {
		name = "游客"
	}
	name = name + "@" + t
	remarkSyn.Lock()
	remark := "<remark><name>" + name + "</name>" + "<msg>" + remarkContent + "</msg></remark>"
	var remarks string
	remarkBytes, err := ioutil.ReadFile(blogDir + "/" + blogId + "/remark/remark.txt")
	if err == nil {
		remarks = string(remarkBytes)
	}
	remarks = remark + "\r\n" + remarks
	_ = ioutil.WriteFile(blogDir+"/"+blogId+"/remark/remark.txt", []byte(remarks), 0655)
	remarkSyn.Unlock()

	http.Redirect(w, r, "read.html?blogId="+blogId, http.StatusFound)
}

var messageSync sync.Mutex

func message2(w http.ResponseWriter, r *http.Request) {
	if !checkAccess(r) {
		http.Redirect(w, r, "message.html", http.StatusFound)
		return
	}
	_ = r.ParseForm()
	//评论
	name := r.FormValue("name")
	remarkContent := r.FormValue("editorContent")
	if len(remarkContent) > 300 {
		http.Redirect(w, r, "message.html", http.StatusFound)
		return
	}
	t := time.Now().Format("2006-01-02 15:04:05")
	if name == "" {
		name = "游客"
	}
	name = name + "@" + t
	messageSync.Lock()
	remark := "<remark><name>" + name + "</name>" + "<msg>" + remarkContent + "</msg></remark>"
	var remarks string
	remarkBytes, err := ioutil.ReadFile("message/message.txt")
	if err == nil {
		remarks = string(remarkBytes)
	}
	remarks = remark + "\r\n" + remarks
	_ = ioutil.WriteFile("message/message.txt", []byte(remarks), 0655)
	messageSync.Unlock()

	http.Redirect(w, r, "message.html", http.StatusFound)
}

func getFormValue(p string, req *http.Request) string {
	v, f := req.Form[p]
	if !f {
		log.Println("..接收参数异常:" + p + "  ")
		return ""
	}
	return v[0]
}
